﻿//$> node gen.js

var prefix = __dirname + "\\";
var fs = require('fs');

main();

function main() {

    // 生成 TokenType
    var content = fs.readFileSync(prefix + 'TokenType.txt', 'utf-8');
    var data = parseTokenType(content);

    updateFile('../Parser/TokenType.cs', function (content) {
        return generateTokenType(content, data);
    });

    updateFile('../Parser/Lexer.cs', function (content) {
        return generateLexer(content, data);
    });

    console.log("done");

}

// TokenType

function parseTokenType(content) {

    var id = 0;

    function parseSignleLine(content) {
        content = content.trim();

        // 忽略注释行。
        if (!content || content.charAt(0) == '/') {
            return null;
        }

        // 处理标题行。
        if (content.charAt(0) == '#') {
            content = content.replace(/^#+\s*/, "");
            return {
                type: 'region',
                isRegion: true,
                name: content
            };
        }

        var result = {
            type: 'field',
            isVirtual: content.charAt(0) == '-'
        };
        var fields = result.isVirtual ? ['fieldName'] : ['fieldName', 'name', 'precedence'];
        for (var i = 0; i < fields.length; i++) {
            var p = content.indexOf(' ');
            if (p <= 0) {
                result[fields[i]] = content;
                content = '';
                break;
            }
            result[fields[i]] = content.substr(0, p);
            content = content.substr(p + 1);
        }

        if (result.isVirtual) {
            result.fieldName = result.fieldName.substr(1);
            result.value = id;
            result.summary = content || result.fieldName;
        } else {
            result.isItem = true;
            result.value = id++;

            result.name = result.name || result.fieldName.replace(/^@/, "");
            result.summary = content || result.name;
            result.isKeyword = isKeyword(result.name);
        }

        return result;
    }

    content = content.split(/\r?\n/);

    var result = [];

    for (var i = 0; i < content.length; i++) {
        var data = parseSignleLine(content[i]);
        if (data) {
            if (data.type == 'region') {
                result.push({
                    type: 'endRegion',
                    isRegion: true
                });
            }
            result.push(data);
        }
    }

    result.push(result.shift());

    // 将 START 的位置移动到下一格。
    for (var i = result.length; i >= 0; i--) {
        if ((result[i - 1] || {}).isVirtual && /START$/.test(result[i - 1].fieldName)) {
            var old = result[i - 1];
            old.value = result[i].fieldName;
            result[i - 1] = result[i];
            result[i] = old;
        }
    }

    // 将 End  的位置移动到下一格。
    for (var i = 0; i < result.length; i++) {
        if (result[i].isVirtual && /END/.test(result[i].fieldName) && (+result[i].value >= +(result[i + 1] || {}).value || (result[i + 1] || {}).isRegion)) {
            var old = result[i + 1];
            result[i + 1] = result[i];
            result[i] = old;
        }
    }

    // 最后一个 END 不应该在 REGION 外。
    if (result.length  > 2 && result[result.length - 1].isVirtual && result[result.length - 2].type == 'endRegion') {
        var old = result[result.length - 1];
        result[result.length - 1] = result[result.length - 2];
        result[result.length - 2] = old;
    }

    console.log("token count = " + id);

    return result;

}

function isKeyword(name) {
    return /^\w/.test(name);
}

function generateTokenType(input, data) {

    function generateItems(data, indent) {
        var result = [];
        for (var i = 0; i < data.length; i++) {
            if (data[i].type == 'field') {
                result.push('\r\n');
                result.push(genIndents(indent) + '/// <summary>\r\n');
                result.push(genIndents(indent) + '/// ' + data[i].summary + '\r\n');
                result.push(genIndents(indent) + '/// </summary>\r\n');
                result.push(genIndents(indent) + data[i].fieldName + ' = ' + data[i].value + ',\r\n');
            } else if (data[i].type == 'region') {
                result.push('\r\n');
                result.push(genIndents(indent) + '#region ' + data[i].name + '\r\n');
            } else if (data[i].type == 'endRegion') {
                result.push('\r\n');
                result.push(genIndents(indent) + '#endregion\r\n');
            }
        }
        result = result.join('');
        return result;
    }

    function generateNames(data, indent) {
        var result = [];
        for (var i = 0; i < data.length; i++) {
            if (data[i].isItem) {
                result.push('"' + data[i].name.replace('\"', "\\\"") + '"');
            }
        }
        result = genIndents(indent) + result.join(',\r\n' + genIndents(indent));
        return result;
    }

    function generatePrecedences(data, indent) {
        var result = [];
        for (var i = 0; i < data.length; i++) {
            if (data[i].isItem) {
                result.push('\r\n');
                result.push(genIndents(indent) + '// ' + data[i].name + '\r\n');
                result.push(genIndents(indent) + data[i].precedence + ', \r\n');
            }
        }
        result = result.join('');
        return result;
    }

    input = replaceRegion(input, '枚举项', generateItems(data, 2));
    input = replaceRegion(input, '优先级列表', generatePrecedences(data, 3));
    input = replaceRegion(input, '名字列表', generateNames(data, 3));

    return input;

}

function generateLexer(input, data) {

    function generateKeywordLexer(data, indent) {

        var result = [];
        for (var i = 0; i < data.length; i++) {
            if (data[i].isKeyword) {
                result.push(data[i]);
            }
        }
        result.sort(function (x, y) { return x.name.length - y.name.length });

        var items = {};
        for (var i = 0; i < result.length; i++) {
            var len = result[i].name.length;
            items[len] = items[len] || {};
            items[len][result[i].name] = result[i].fieldName;
        }

        result = '';

        for (var len in items) {
            result += '\r\n' + genIndents(indent) + '#region ' + len + '\r\n\r\n';
            result += genIndents(indent) + 'case ' + len + ':\r\n';

            generateSingleLength(items[len], indent + 1);

            result += genIndents(indent + 1) + 'break;\r\n';
            result += '\r\n' + genIndents(indent) + '#endregion\r\n';
        }

        function generateSingleLength(item, indent) {

            var arr = [];
            for (var i in item) {
                arr.push(i);
            }
            arr.sort();
            arr = mergeSamePrefix(arr, 0);
            genSwitch(arr, indent, 0);
            
            function mergeSamePrefix(arr, index) {
                // [ 'as', 'if', 'is' ]
                var t = {};
                for (var i = 0; i < arr.length; i++) {
                    var c = arr[i].charAt(index);
                    t[c] = t[c] || [];
                    t[c].push(arr[i]);
                }
                // {'a' : ['as'], 'i': ['if', 'is']}
                
                for (var c in t) {
                    if (t[c].length >= 2) {
                        t[c] = mergeSamePrefix(t[c], index + 1);
                    } else {
                        t[c] = t[c][0];
                    }
                }
                // {'a' : 'as', 'i': {'f': 'if', 's': 'is'}}
                
                return t;
            }

            function genSwitch(arr, indent, index) {

                // 无其它歧义项，直接判断之后的字符是否都满足要求即可。
                if (typeof arr === 'string') {

                    var conditions = [];
                    for (var i = index; i < arr.length; i++) {
                        conditions.push('buffer[' + i + '] == \'' + arr.charAt(i) + '\'')
                    }
                    if (conditions.length > 0) {
                        result += genIndents(indent++) + 'if(' + conditions.join(' && ') + ') { \r\n';
                    }

                    result += genIndents(indent) + '_scanTargetToken.type = TokenType.' + item[arr] + ';\r\n';
                    result += genIndents(indent) + 'goto end;\r\n';
                    if (conditions.length > 0) {
                        result += genIndents(--indent) + '}\r\n';
                    }

                    return conditions.length == 0 ? false : true;
                }

                result += genIndents(indent++) + 'switch (buffer[' + index + ']) {\r\n';

                for (var c in arr) {
                    result += genIndents(indent) + 'case \'' + c + '\':\r\n';
                    if (genSwitch(arr[c], indent + 1, index + 1) !== false) {
                        result += genIndents(indent + 1) + 'break;\r\n';
                    }
                }

                result += genIndents(--indent) + '}\r\n';
            }

        }

        return result;
    }

    input = replaceRegion(input, '关键字', generateKeywordLexer(data, 5));
    return input;
}

// Utility

function replaceRegion(input, regionName, newContent) {
    return input.replace(new RegExp("((\\s*)\\#region\\s+" + regionName + ")([\\s\\S]*)(\\#endregion\\s+" + regionName + ")"), "$1$2// This region is generated by a tool. DO NOT EDIT!!!\r\n" + newContent + "$2$4");
}

function genIndents(indent) {
    var result = '';
    while (indent-- > 0)
        result += '\t';
    return result;
}

function updateFile(fileName, updater) {
    var content = fs.readFileSync(prefix + fileName, 'utf-8');
    content = updater(content);
    fs.writeFileSync(prefix + fileName, content, 'utf-8');
}